use crate::db::malines::KV;
use crate::db::FUDB;
///
use crate::model::kline::Kline;
use crate::model::malines::ma::MAConfig;
use crate::model::malines::ma::PeriodV;
use crate::model::malines::ma::MA;
use crate::round2;
use anyhow::anyhow;
use anyhow::ensure;
use anyhow::Result;
use surrealdb::sql::Id;
use surrealdb::sql::Thing;
use yata::core::Method;
use yata::helpers::Peekable;
use yata::methods::EMA;

/// Exponential Moving Average of specified length for timeseries of type ValueType
pub struct Ema {
    /// ema computer for each period
    emas: Vec<(u8, EMA)>,
}

impl Ema {
    pub fn new(ma_config: &MAConfig, init_value: &f64) -> Result<Self> {
        let emas: Vec<(u8, EMA)> = ma_config.periods
            .iter()
            .map(|&period| Self::create_ema(period, init_value))
            .collect::<Result<Vec<_>, _>>()?;
        Ok(Ema { emas })
    }

    pub fn next(&mut self, value: &f64) -> MA<f64> {
        Self::compute(&mut self.emas, value)
    }

    pub fn repeat(&mut self, value: &f64) -> MA<f64> {
        let mut emas = self.emas.clone();
        Self::compute(&mut emas, value)
    }

    fn compute(emas: &mut Vec<(u8, EMA)>, value: &f64) -> MA<f64> {
        for ema in emas.iter_mut() {
            ema.1.next(value);
        }

        let mas: Vec<PeriodV<f64>> = emas
            .iter()
            .map(|(period, ema)| PeriodV {
                period: period.clone() as u16,
                value: ema.peek(),
            })
            .collect();

        let ma = MA { values: mas };
        return ma;
    }

    // Function to create an EMA instance and handle potential errors.
    pub fn create_ema(period: u8, initial_value: &f64) -> Result<(u8, EMA)> {
        let rs = EMA::new(period, initial_value).map_err(|e|
            anyhow!("Failed to create EMA: {}", e)
        );
        match rs {
            Ok(rs) => Ok((period, rs)),
            Err(e) => Err(e),
        }
    }
}

/// 更新指定K线数据的EMA,指定MA步长,现在只能做全量计算
///
/// @param klines: K线数据
///
/// @param config: EMA设置
///
/// return 更新的记录数
///

pub async fn update_ema(klines: &Vec<Kline>, config: &Option<MAConfig>) -> Result<u32> {
    ensure!(klines.len() > 0, "klines is empty");

    let mut updates = 0u32;
    if let Some(fstk) = klines.first() {
        let mut ma_values: Vec<KV<MA<f64>>> = Vec::new();
        let ma_config = config.as_ref().cloned().unwrap_or_default();
        let mut emas: Vec<(u8, EMA)> = ma_config.periods
            .iter()
            .map(|&period| Ema::create_ema(period, &fstk.close))
            .collect::<Result<Vec<_>, _>>()?;

        for (i, kline) in klines.iter().enumerate() {
            if let Some(Thing { id: Id::String(kid_str), .. }) = &kline.id {
                for ema in emas.iter_mut() {
                    ema.1.next(&kline.close);
                }

                let mas: Vec<PeriodV<f64>> = emas
                    .iter()
                    .map(|(period, ema)| PeriodV {
                        period: period.clone() as u16,
                        value: round2(ema.peek()),
                    })
                    .collect();

                let ma = MA { values: mas };

                println!("kline[{i}] close={} ==> {:?}", kline.close, ma);

                // let updated: Option<FudaRecord> = FUDB
                //     .update(("kline", kid_str))
                //     .patch(PatchOp::replace("/ema", ma.clone()))
                //     .await
                //     .map_err(|err| anyhow!(err))?;

                ma_values.push(KV {
                    id: kline.id.clone().unwrap(),
                    value: ma,
                });

                // if let Some(_) = updated {
                updates += 1;
                // }
            }
        }

        //TODO, use surrealdb Statement For loop
        let query = FUDB.query(
            r#"FOR $kv IN $ma_values { UPDATE $kv.id SET l_ema = $kv.value ;}"#
        ).bind(("ma_values", ma_values.clone()));

        // println!(
        //     "(ema::update_ema) query={:?} \n\nma_values={:?}\n",
        //     query, ma_values
        // );
        let rs = query.await?;
        // println!("(ema::update_ema) rs= {:?}", rs);
    }

    Ok(updates)
}

#[cfg(test)]
mod test {
    use chrono::Utc;

    use crate::db;
    use crate::db::malines::ema::Ema;
    use crate::model::kline::Klt;
    use crate::model::kline::KLINE_COMMON_FIELDS;
    use crate::model::malines::ma::MAConfig;
    use crate::model::malines::ma::PeriodV;
    use crate::model::malines::ma::MA;

    #[tokio::test]
    async fn test_ema_next() {
        let mut ema = Ema::new(
            &(MAConfig {
                periods: [3].to_vec(),
            }),
            &3.0
        ).unwrap();

        ema.next(&3.0);
        ema.next(&6.0);
        assert_eq!(ema.next(&9.0), MA::<f64> {
            values: [
                PeriodV {
                    period: 3,
                    value: 6.75,
                },
            ].to_vec(),
        });
        assert_eq!(ema.next(&12.0), MA::<f64> {
            values: [
                PeriodV {
                    period: 3,
                    value: 9.375,
                },
            ].to_vec(),
        });
    }

    #[tokio::test]
    async fn test_update_ema() {
        let _ = db::init().await.unwrap();
        let ema_config = Some(MAConfig::default());

        let klt_vec = vec![
            // Klt::M005,
            // Klt::M015,
            // Klt::M030,
            // Klt::M060,
            Klt::DAY
            // Klt::WEEK,
            // Klt::MONTH,
        ];
        for klt in &klt_vec {
            let klines = db::kline
                ::select("SZ.KZZ.128136", None, Some(klt), Some("klts ASC"), 0, 10000).await
                .unwrap();
            let start = Utc::now();
            let result = db::malines::ema::update_ema(&klines, &ema_config).await;
            let end = Utc::now();
            println!(
                "\n\nupdate_ema spent time= {:?} for klines.len()= {}",
                end - start,
                klines.len()
            );

            println!("(test_update_ema) result= {:?}", result);
        }

        let result = db::kline::select(
            "SZ.KZZ.128136",
            None,
            Some(&Klt::M005),
            Some("klts ASC"),
            0,
            100
        ).await;
        if let Ok(klines) = &result {
            for (index, kline) in klines.iter().enumerate() {
                if index > 3 {
                    break;
                }
                // println!(
                //     "[{:02}], close:{:.2}=>l_ema:{:?}",
                //     index, kline.close, kline.malines,
                // );
            }
        }
    }
}
// kline[62] close=212.745 ==> MA { mas: [PeriodMA { period: 5, value: 203.64 }, PeriodMA { period: 10, value: 203.2 }, PeriodMA { period: 20, value: 205.36 }, PeriodMA { period: 30, value: 204.28 }, PeriodMA { period: 60, value: 189.0 }, PeriodMA { period: 72, value: 182.3 }, PeriodMA { period: 89, value: 173.75 }, PeriodMA { period: 120, value: 161.2 }, PeriodMA { period: 250, value: 133.79 }] }
// kline[63] close=197.78 ==> MA { mas: [PeriodMA { period: 5, value: 201.68 }, PeriodMA { period: 10, value: 202.22 }, PeriodMA { period: 20, value: 204.64 }, PeriodMA { period: 30, value: 203.86 }, PeriodMA { period: 60, value: 189.28 }, PeriodMA { period: 72, value: 182.72 }, PeriodMA { period: 89, value: 174.28 }, PeriodMA { period: 120, value: 161.8 }, PeriodMA { period: 250, value: 134.3 }] }
// kline[64] close=274.431 ==> MA { mas: [PeriodMA { period: 5, value: 225.93 }, PeriodMA { period: 10, value: 215.35 }, PeriodMA { period: 20, value: 211.29 }, PeriodMA { period: 30, value: 208.42 }, PeriodMA { period: 60, value: 192.08 }, PeriodMA { period: 72, value: 185.23 }, PeriodMA { period: 89, value: 176.5 }, PeriodMA { period: 120, value: 163.67 }, PeriodMA { period: 250, value: 135.42 }] }
// kline[65] close=203.376 ==> MA { mas: [PeriodMA { period: 5, value: 218.41 }, PeriodMA { period: 10, value: 213.17 }, PeriodMA { period: 20, value: 210.53 }, PeriodMA { period: 30, value: 208.09 }, PeriodMA { period: 60, value: 192.45 }, PeriodMA { period: 72, value: 185.73 }, PeriodMA { period: 89, value: 177.1 }, PeriodMA { period: 120, value: 164.32 }, PeriodMA { period: 250, value: 135.96 }] }
// kline[66] close=179.825 ==> MA { mas: [PeriodMA { period: 5, value: 205.55 }, PeriodMA { period: 10, value: 207.11 }, PeriodMA { period: 20, value: 207.61 }, PeriodMA { period: 30, value: 206.27 }, PeriodMA { period: 60, value: 192.03 }, PeriodMA { period: 72, value: 185.57 }, PeriodMA { period: 89, value: 177.16 }, PeriodMA { period: 120, value: 164.58 }, PeriodMA { period: 250, value: 136.31 }] }
// kline[67] close=167.199 ==> MA { mas: [PeriodMA { period: 5, value: 192.77 }, PeriodMA { period: 10, value: 199.85 }, PeriodMA { period: 20, value: 203.76 }, PeriodMA { period: 30, value: 203.75 }, PeriodMA { period: 60, value: 191.22 }, PeriodMA { period: 72, value: 185.07 }, PeriodMA { period: 89, value: 176.94 }, PeriodMA { period: 120, value: 164.62 }, PeriodMA { period: 250, value: 136.56 }] }
// kline[68] close=183.383 ==> MA { mas: [PeriodMA { period: 5, value: 189.64 }, PeriodMA { period: 10, value: 196.86 }, PeriodMA { period: 20, value: 201.82 }, PeriodMA { period: 30, value: 202.43 }, PeriodMA { period: 60, value: 190.96 }, PeriodMA { period: 72, value: 185.02 }, PeriodMA { period: 89, value: 177.08 }, PeriodMA { period: 120, value: 164.93 }, PeriodMA { period: 250, value: 136.93 }] }
// kline[69] close=158.072 ==> MA { mas: [PeriodMA { period: 5, value: 179.12 }, PeriodMA { period: 10, value: 189.81 }, PeriodMA { period: 20, value: 197.65 }, PeriodMA { period: 30, value: 199.57 }, PeriodMA { period: 60, value: 189.88 }, PeriodMA { period: 72, value: 184.28 }, PeriodMA { period: 89, value: 176.66 }, PeriodMA { period: 120, value: 164.82 }, PeriodMA { period: 250, value: 137.1 }] }
